﻿using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;

namespace SSG.Tools
{
    /// <summary>
    ///   Misc. <see cref="string" /> tools.
    /// </summary>
    public static class StringTools
    {
        #region Field / Properties
        private static readonly CultureInfo CultureEnglish = new CultureInfo("en-GB");
        private static readonly CultureInfo CultureGerman = new CultureInfo("de-DE");
        private static readonly NumberFormatInfo CurrentNumberFormatInfo = CultureInfo.CurrentCulture.NumberFormat;
        private static readonly Regex EmailRegEx = new Regex(@"^([a-zA-Z0-9_\-\.]+)@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([a-zA-Z0-9\-]+\.)+))((?i:localhost)|[a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$");
        private static readonly Regex MultipleWhitespacesRegEx = new Regex(@"\s+");
        private static readonly Regex MultipleBackslashRegEx = new Regex(@"\\+");
        #endregion Field / Properties

        #region Methods
        /// <summary>
        ///   Abbreviate a String, e.g. "BlaBlaFasel" -> "Bla ..."
        /// </summary>
        /// <param name="inString"> </param>
        /// <param name="length"> </param>
        /// <param name="app"> </param>
        /// <returns> </returns>
        public static String AbbreviateString(this String inString, int length, String app = "...")
        {
            int applength = (String.IsNullOrEmpty(app)) ? 0 : app.Length;
            if (String.IsNullOrEmpty(inString) || length < 1) return "";
            if (inString.Length <= length) return inString; // no abbreviation required

            // at leat 5 char length for String
            if (length < (applength + 4)) return inString.Substring(0, length);
            return inString.Substring(0, length - applength) + app;
        }

        /// <summary>
        ///   Abbreviate a String, e.g. "BlaBlaFasel" -> "Bla ... Fasel"
        /// </summary>
        /// <param name="inString"> </param>
        /// <param name="prefixLength"> </param>
        /// <param name="postfixLength"> </param>
        /// <param name="app"> </param>
        /// <returns> </returns>
        public static String AbbreviateStringInMiddle(this String inString, int prefixLength = 5, int postfixLength = 5, String app = "...")
        {
            if (String.IsNullOrEmpty(inString) || NullSafeLength(inString) <= prefixLength + postfixLength) return inString;
            string abbString = inString.Substring(0, prefixLength) + app + inString.Substring(inString.Length - postfixLength);
            return abbString;
        }

        /// <summary>
        ///   Is string a valid integer?
        /// </summary>
        /// <param name="value"> </param>
        /// <returns> </returns>
        public static bool IsInteger(this string value)
        {
            if (String.IsNullOrEmpty(value)) return false;
            try {
                int result;
                return int.TryParse(value, out result);
            }
            catch {
                return false;
            }
        }

        /// <summary>
        ///   Is <paramref name="emailCandidate" /> a valid email address?
        /// </summary>
        /// <param name="emailCandidate"> </param>
        /// <returns> </returns>
        [Pure]
        public static bool IsValidEmailAddress(this string emailCandidate)
        {
            return !String.IsNullOrEmpty(emailCandidate) && (EmailRegEx.IsMatch(emailCandidate));
        }

        /// <summary>
        ///   Parse an integer number string with defaults for <c>null</c> / error.
        /// </summary>
        /// <param name="integerCandidate"> </param>
        /// <param name="nullValue"> </param>
        /// <param name="errorValue"> </param>
        /// <returns> </returns>
        public static int ParseIntegerSafe(this string integerCandidate, int nullValue, int errorValue)
        {
            int result;
            if (String.IsNullOrEmpty(integerCandidate)) return nullValue;
            return int.TryParse(integerCandidate, out result) ? result : errorValue;
        }

        /// <summary>
        ///   Valid long number?
        /// </summary>
        /// <param name="value"> </param>
        /// <returns> </returns>
        public static bool IsLong(string value)
        {
            if (String.IsNullOrEmpty(value)) return false;
            try {
                long result;
                return long.TryParse(value, out result);
            }
            catch {
                return false;
            }
        }

        /// <summary>
        ///   Parse a long number string with defaults for <c>null</c> / error.
        /// </summary>
        /// <param name="longCandidate"> </param>
        /// <param name="nullValue"> </param>
        /// <param name="errorValue"> </param>
        /// <returns> </returns>
        public static long ParseLongSafe(this string longCandidate, int nullValue, int errorValue)
        {
            if (String.IsNullOrEmpty(longCandidate)) return nullValue;
            long result;
            return long.TryParse(longCandidate, out result) ? result : errorValue;
        }

        /// <summary>
        ///   Maximum number of characters in a string list
        ///   (aka longest word).
        /// </summary>
        /// <param name="list"> </param>
        /// <returns> </returns>
        public static int MaximalCharacters(this List<string> list)
        {
            if (list == null || list.Count < 1) return 0;
            return list.Max(s => s.Length);
        }

        /// <summary>
        ///   The first element of a string list. Sometimes useful to use this one
        ///   instead of List[0]
        /// </summary>
        /// <param name="stringList"> </param>
        /// <returns> </returns>
        public static string GetFirst(this List<string> stringList)
        {
            if (stringList == null || stringList.Count < 1) return null;
            return stringList[0];
        }

        /// <summary>
        ///   An enumeration converted into a string.
        ///   Newline such as <see cref="Environment.NewLine" /> is possible.
        /// </summary>
        /// <param name="enumerator"> </param>
        /// <param name="seperator"> </param>
        /// <param name="appendix"> </param>
        /// <param name="withIndex"> </param>
        /// <param name="checkElementForSeperator"> Check whether the enumeration element contains the seperator </param>
        /// <param name="replaceSeperatorInElement"> If the enumeration element contains the seperator, it will be replaced. <c>null</c> means no replacemenet </param>
        /// <param name="prefix"> </param>
        /// <returns> </returns>
        /// <seealso cref="CollectionTools.SplitList{T}" />
        public static string EnumerationToString<T>(this IEnumerable<T> enumerator, string seperator = ", ", string prefix = null, string appendix = null, bool withIndex = false, bool checkElementForSeperator = false, string replaceSeperatorInElement = null)
        {
            // ReSharper disable PossibleMultipleEnumeration
            if (enumerator == null || enumerator.NullSafeIsEmpty()) return null;
            if (String.IsNullOrEmpty(seperator)) throw new ArgumentNullException("seperator");
            if (replaceSeperatorInElement != null && seperator.Equals(replaceSeperatorInElement)) throw new ArgumentException("Seperator " + seperator + " and replacement " + replaceSeperatorInElement + " are the same");

            StringBuilder sb = new StringBuilder();
            int i = 0;
            foreach (T obj in enumerator) {
                if (obj.IsNullOrDefault()) continue;
                string s = obj.ToString();
                if (!String.IsNullOrEmpty(s) && s.Contains(seperator)) {
                    if (replaceSeperatorInElement != null) {
                        s = s.Replace(seperator, replaceSeperatorInElement);
                    }
                    else if (checkElementForSeperator) {
                        throw new ArgumentException("The following element " + s + " contains the seperator " + seperator);
                    }
                }
                if (prefix != null) sb.Append(prefix);
                if (withIndex) {
                    sb.Append(i);
                    sb.Append(": ");
                }
                sb.Append(s);
                if (appendix != null) sb.Append(appendix);
                if (i + 1 < enumerator.Count()) sb.Append(seperator);
                i++;
            }
            // ReSharper restore PossibleMultipleEnumeration
            return sb.ToString();
        }

        /// <summary>
        ///   Split a string into a list.
        /// </summary>
        /// <param name="toBeSplit"> </param>
        /// <param name="seperator"> </param>
        /// <param name="trim"> trim the single values </param>
        /// <returns> </returns>
        public static List<string> StringToList(this string toBeSplit, string seperator, bool trim)
        {
            if (toBeSplit == null) return null;
            string[] sp = toBeSplit.Split(new[] {seperator}, StringSplitOptions.RemoveEmptyEntries);
            if (trim) {
                for (int i = 0; i < sp.Length; i++) {
                    sp[i] = sp[i].Trim();
                }
            }
            return new List<string>(sp);
        }

        /// <summary>
        ///   <see cref="int" /> to n-digit string (leading zeros)
        /// </summary>
        /// <param name="v"> </param>
        /// <param name="digits"> </param>
        /// <returns> </returns>
        public static string IntToNDigits(this int v, int digits)
        {
            string sv = v.ToString(CultureInfo.InvariantCulture);
            if (sv.Length < digits) {
                sv = sv.PadLeft(digits, '0');
            }
            return sv;
        }

        /// <summary>
        ///   Extract the filetype of a filename, e.g. myGig.gif 
        ///   returns .gif
        /// </summary>
        /// <param name="fileName"> </param>
        /// <returns> </returns>
        public static string ExtractFiletype(this string fileName)
        {
            if (fileName == null || !fileName.Contains(".") || fileName.EndsWith(".")) return null;
            int p = fileName.LastIndexOf('.');
            return fileName.Substring(p + 1);
        }

        /// <summary>
        ///   Replace \n to newline for GUI
        /// </summary>
        /// <param name="candidate"> </param>
        /// <returns> </returns>
        public static string ReplaceBackslashNWithNewline(this string candidate)
        {
            return candidate == null ? null : candidate.Replace("\n", Environment.NewLine);
        }

        /// <summary>
        ///   Build a string hierarchy for n levels (inner exceptions)
        /// </summary>
        /// <param name="e"> </param>
        /// <param name="level"> </param>
        /// <returns> </returns>
        public static string ExceptionMessageHierarchy(this Exception e, int level)
        {
            if (level < 1 || e == null) return null;
            StringBuilder sb = new StringBuilder(e.Message);
            Exception ie = e;
            for (int i = 1; i < level; i++) {
                ie = ie.InnerException;
                if (ie == null) break;
                sb.Append(" / ");
                sb.Append(ie.Message);
            }
            return sb.ToString();
        }

        /// <summary>
        ///   Convert to upper if not <c>null</c>.
        /// </summary>
        /// <param name="candidate"> </param>
        /// <returns> </returns>
        public static string NullSafeToUpper(this string candidate)
        {
            return (String.IsNullOrEmpty(candidate) ? candidate : candidate.ToUpper());
        }

        /// <summary>
        ///   Reverse <paramref name="inString" />, e.g. "123" -> "321"
        /// </summary>
        /// <param name="inString"> </param>
        /// <returns> </returns>
        public static string Reverse(this string inString)
        {
            if (String.IsNullOrEmpty(inString) || inString.Length == 1) return inString;
            char[] c = inString.ToCharArray();
            Array.Reverse(c);
            return new string(c);
        }

        /// <summary>
        ///   Convert to upper if not <c>null</c> and trim.
        /// </summary>
        /// <param name="candidate"> </param>
        /// <returns> </returns>
        public static string NullSafeToUpperAndTrim(this string candidate)
        {
            return String.IsNullOrEmpty(candidate) ? candidate : candidate.Trim().ToUpper();
        }

        /// <summary>
        ///   Trim if not <c>null</c>.
        /// </summary>
        /// <param name="candidate"> </param>
        /// <returns> </returns>
        public static string NullSafeTrim(this string candidate)
        {
            return String.IsNullOrEmpty(candidate) ? candidate : candidate.Trim();
        }

        /// <summary>
        ///   Compare strings, but treat <c>""</c> same as <c>null</c> (lenient check).
        ///   Case can be <paramref name="ignoreCase">ignored</paramref>.
        /// </summary>
        /// <param name="stringA"> </param>
        /// <param name="stringB"> </param>
        /// <param name="ignoreCase"> </param>
        /// <returns> </returns>
        public static bool NullSafeLenientEquals(this string stringA, string stringB, bool ignoreCase)
        {
            if (stringA == stringB) return true;
            if (String.IsNullOrEmpty(stringA) && String.IsNullOrEmpty(stringB)) return true;
            if (stringA == null) return false;
            bool r = ignoreCase ? stringA.Equals(stringB, StringComparison.CurrentCultureIgnoreCase) : stringA.Equals(stringB);
            return r;
        }

        /// <summary>
        /// Length (<c>null</c> safe).
        /// </summary>
        /// <param name="text"> </param>
        /// <returns> </returns>
        public static int NullSafeLength(this string text)
        {
            return text == null ? 0 : text.Length;
        }

        /// <summary>
        /// Last characters of a string.
        /// </summary>
        /// <param name="text"></param>
        /// <param name="length"></param>
        /// <returns></returns>
        public static string NullSafeLastChars(this string text, int length)
        {
            if (String.IsNullOrEmpty(text) || length < 1) return "";
            if (length > text.Length) return text;
            return (text.Substring(text.Length - length));
        }

        /// <summary>
        ///   Convert a string into a <c>bool</c> expression
        /// </summary>
        /// <param name="candidate"> </param>
        /// <returns> </returns>
        /// <example>
        ///   true: yes, true, X  / false: no, false
        /// </example>
        public static bool StringToBool(this string candidate)
        {
            if (String.IsNullOrEmpty(candidate)) return false;
            string c = candidate.ToLower();

            // check exact matches for true and false
            if ("true".Equals(c) || "yes".Equals(c) || "ja".Equals(c) || "x".Equals(c) || "yep".Equals(c) || "yeah".Equals(c))
                return true;
            if ("false".Equals(c) || "x".Equals(c) || "no".Equals(c) || "n".Equals(c) || "nein".Equals(c) || "nope".Equals(c))
                return false;

            // weak matches
            return c.StartsWith("y") || c.StartsWith("j") || c.StartsWith("t");
        }

        /// <summary>
        ///   Is any of the strings <c>null</c> or empty
        /// </summary>
        /// <param name="strings"> </param>
        /// <returns> </returns>
        public static bool IsAnyNullOrEmpty(params string[] strings)
        {
            if (strings == null || strings.Length < 1) return false;
            return strings.Any(String.IsNullOrEmpty);
        }

        /// <summary>
        ///   Is none of the given strings <c>null</c> or empty
        /// </summary>
        /// <param name="strings"> </param>
        /// <returns> </returns>
        public static bool IsNoneNullOrEmpty(params string[] strings)
        {
            return !StringTools.IsAnyNullOrEmpty(strings);
        }

        /// <summary>
        ///   Remove the trailing string <paramref name="remove" />.
        /// </summary>
        /// <param name="candidate"> </param>
        /// <param name="remove"> </param>
        /// <returns> </returns>
        public static string TrimEnd(this string candidate, string remove)
        {
            if (candidate.NullSafeIsEmpty() || remove.NullSafeIsEmpty() || !candidate.EndsWith(remove)) return candidate;
            return candidate.Substring(0, candidate.Length - remove.Length);
        }

        /// <summary>
        ///   Are all the strings the same (Equal)
        /// </summary>
        /// <param name="candidates"> </param>
        /// <returns> </returns>
        public static bool AllTheSame(params string[] candidates)
        {
            if (candidates == null || candidates.Length < 1) return false;
            if (candidates.Length < 2) return true;
            string c = candidates[0];
            for (int i = 1; i < candidates.Length; i++) {
                if (c == candidates[i]) continue; // same object, both null
                if (c == null || !c.Equals(candidates[i])) return false;
            }
            return true;
        }

        /// <summary>
        ///   Get the first (if there is any) "not-empty" and "not-null"
        ///   string value.
        /// </summary>
        /// <param name="candidates"> </param>
        /// <returns> </returns>
        public static string FirstNotEmptyValue(params string[] candidates)
        {
            if (candidates == null || candidates.Length < 1) return null;
            return candidates.Where(s => s != null).FirstOrDefault(s => !String.IsNullOrEmpty(s.Trim()));
        }

        /// <summary>
        ///   Convert empty string to <c>null</c>
        /// </summary>
        /// <param name="candidate"> </param>
        /// <returns> </returns>
        public static string EmptyStringToNull(this string candidate)
        {
            return ("".Equals(candidate)) ? null : candidate;
        }

        /// <summary>
        ///   A <c>null</c> value will be replace by <c>String.Empty</c>.
        /// </summary>
        /// <param name="candidate"> </param>
        /// <returns> </returns>
        public static string NullToEmpty(this string candidate)
        {
            return candidate ?? String.Empty;
        }

        /// <summary>
        ///   Try to find the best match for a given length
        /// </summary>
        /// <param name="desiredLength"> </param>
        /// <param name="candidates"> </param>
        /// <returns> </returns>
        public static string GetBestLengthMatchOrAbbreviate(int desiredLength, params string[] candidates)
        {
            if (desiredLength < 0) return null;
            if (desiredLength == 0) return "";
            if (candidates == null || candidates.Length < 1) return null;

            string bestOversize = null;
            string bestUndersize = null;
            int oversizeDiff = desiredLength;
            int undersizeDiff = desiredLength;
            foreach (string c in candidates) {
                if (String.IsNullOrEmpty(c)) continue;
                if (c.Length == desiredLength) return c;
                int diff = desiredLength - c.Length;
                if (diff > 0) {
                    // too short
                    if (diff < undersizeDiff) {
                        bestUndersize = c;
                        undersizeDiff = diff;
                    }
                }
                else {
                    diff *= -1;
                    // too long
                    if (diff < oversizeDiff) {
                        bestOversize = c;
                        oversizeDiff = diff;
                    }
                }
            }

            // now rating the difference
            bool hasOs = !String.IsNullOrEmpty(bestOversize);
            bool hasUs = !String.IsNullOrEmpty(bestUndersize);
            int maxUsDiff;
            if (desiredLength < 3) maxUsDiff = 0;
            else if (desiredLength < 4) maxUsDiff = 1;
            else if (desiredLength < 8) maxUsDiff = 2;
            else if (desiredLength < 15) maxUsDiff = 4;
            else maxUsDiff = (desiredLength/4);

            // if the undersize is acceptable, I use the udersize, else I use an abbreviated oversize string
            return (hasUs && undersizeDiff <= maxUsDiff) || !hasOs ? bestUndersize : bestOversize.AbbreviateString(desiredLength);
        }

        /// <summary>
        /// Remove multiple whitespaces.
        /// </summary>
        /// <param name="text"> </param>
        /// <returns> </returns>
        public static string RemoveMultipleWhiteSpaces(this string text)
        {
            return String.IsNullOrEmpty(text) ? text : StringTools.MultipleWhitespacesRegEx.Replace(text, " ");
        }

        /// <summary>
        /// Remove multiple backsplashes.
        /// </summary>
        /// <param name="text"> </param>
        /// <returns> </returns>
        public static string RemoveMultipleBackslashes(this string text)
        {
            return String.IsNullOrEmpty(text) ? text : StringTools.MultipleBackslashRegEx.Replace(text, "\\");
        }

        /// <summary>
        ///   Count a particular <paramref name="c" />.
        /// </summary>
        /// <param name="candidate"> </param>
        /// <param name="c"> </param>
        /// <returns> </returns>
        public static int CountChar(this string candidate, char c)
        {
            if (String.IsNullOrEmpty(candidate)) return 0;
            char[] chars = candidate.ToCharArray();
            int counter = 0;
            // ReSharper disable LoopCanBeConvertedToQuery
            foreach (char cc in chars) {
                if (cc == c) counter++;
            }
            // ReSharper restore LoopCanBeConvertedToQuery
            return counter;
        }

        /// <summary>
        ///   Remove <paramref name="numberOfCharsToRemove" /> characters from <paramref name="text" />.
        /// </summary>
        /// <param name="text"> </param>
        /// <param name="numberOfCharsToRemove"> </param>
        /// <returns> </returns>
        public static string RemoveLastCharacters(this string text, int numberOfCharsToRemove)
        {
            if (text == null) throw new ArgumentNullException("text");
            if (numberOfCharsToRemove < 0 || numberOfCharsToRemove > text.Length) throw new ArgumentOutOfRangeException("numberOfCharsToRemove");
            return numberOfCharsToRemove == 0 ? text : text.Substring(0, text.Length - numberOfCharsToRemove);
        }

        /// <summary>
        ///   Remove substring <paramref name="remove" /> from <paramref name="text" />.
        /// </summary>
        /// <param name="text"> </param>
        /// <param name="remove"> </param>
        /// <returns> </returns>
        public static string RemoveString(this string text, string remove)
        {
            if (String.IsNullOrEmpty(text) || String.IsNullOrEmpty(remove) || !text.Contains(remove)) return text;
            return text.Replace(remove, "");
        }

        /// <summary>
        ///   A version string <c>Major.Minor.Build.Revision</c> split in its parts.
        /// </summary>
        /// <param name="version"> </param>
        /// <returns> </returns>
        public static List<string> SplitVersionString(this string version)
        {
            if (String.IsNullOrEmpty(version)) throw new ArgumentNullException("version");
            Contract.EndContractBlock();
            string[] parts = version.Split(new[] {'.'}, 4);
            List<string> partsList = new List<string>(4);
            for (int i = 0; i < 4; i++) {
                partsList.Add(parts.Length <= i ? "" : parts[i]);
            }
            return partsList;
        }

        /// <summary>
        ///   As far it is possible the parts of a version (<c>Major.Minor.Build.Revision</c>) 
        ///   will be converted to <see cref="int" />s.
        /// </summary>
        /// <param name="version"> </param>
        /// <returns> </returns>
        public static List<int> SplitVersionStringToInt(this string version)
        {
            if (String.IsNullOrEmpty(version)) throw new ArgumentNullException("version");
            Contract.EndContractBlock();

            List<string> p = version.SplitVersionString();
            List<int> partsList = new List<int>(4);
            for (int i = 0; i < 4; i++) {
                int pi;
                if (int.TryParse(p[i], out pi))
                    partsList.Add(pi);
                else
                    partsList.Add(-1);
            }
            return partsList;
        }

        /// <summary>
        ///   Remove character <paramref name="remove" /> from <paramref name="text" />.
        /// </summary>
        /// <param name="text"> </param>
        /// <param name="remove"> </param>
        /// <returns> </returns>
        public static string RemoveCharacter(this string text, char remove)
        {
            if (String.IsNullOrEmpty(text) || !text.Contains(remove)) return text;
            char[] removed = new char[text.Length];
            int i = 0;
            foreach (char c in text) {
                if (c != remove) removed[i++] = c;
            }
            return i == 0 ? "" : new string(removed, 0, i);
        }

        /// <summary>
        ///   Fast case insensitive replacement.
        /// </summary>
        /// <param name="original"> </param>
        /// <param name="pattern"> </param>
        /// <param name="replacement"> </param>
        /// <returns> </returns>
        //  EXTERNAL: http://www.codeproject.com/KB/string/fastestcscaseinsstringrep.aspx
        public static string ReplaceCaseInsensitive(this string original, string pattern, string replacement)
        {
            int position0, position1;
            int count = position0 = 0;
            string upperString = original.ToUpper();
            string upperPattern = pattern.ToUpper();
            int inc = (original.Length/pattern.Length)*(replacement.Length - pattern.Length);
            char[] chars = new char[original.Length + Math.Max(0, inc)];
            while ((position1 = upperString.IndexOf(upperPattern, position0, System.StringComparison.Ordinal)) != -1) {
                // ReSharper disable ForCanBeConvertedToForeach
                for (int i = position0; i < position1; ++i) chars[count++] = original[i];
                for (int i = 0; i < replacement.Length; ++i) chars[count++] = replacement[i];
                // ReSharper restore ForCanBeConvertedToForeach
                position0 = position1 + pattern.Length;
            }
            if (position0 == 0) return original;
            for (int i = position0; i < original.Length; ++i) chars[count++] = original[i];
            return new string(chars, 0, count);
        }

        /// <summary>
        ///   Parses the number in multiple formats.
        ///   the current locale.
        /// </summary>
        /// <param name="numberString"> </param>
        /// <param name="number"> </param>
        /// <returns> </returns>
        public static bool TryParseCommonLocales(this string numberString, out double number)
        {
            number = default(double);
            if (String.IsNullOrEmpty(numberString)) return false;
            if (double.TryParse(numberString, NumberStyles.Number, StringTools.CurrentNumberFormatInfo, out number)) {
                // all OK, I can get the number in the current locale
                return true;
            }

            // I cannot parse, try another locale
            CultureInfo alternativeCulture;
            if (CultureInfo.CurrentCulture.Name.ToLower().Contains("de"))
                alternativeCulture = StringTools.CultureEnglish;
            else if (CultureInfo.CurrentCulture.Name.ToLower().Contains("en"))
                alternativeCulture = StringTools.CultureGerman;
            else
                return false;

            // try 2nd locale
            return double.TryParse(numberString, NumberStyles.Number, alternativeCulture.NumberFormat, out number);
        }

        /// <summary>
        ///   Append string <paramref name="appendMe" /> is not empty.
        /// </summary>
        /// <param name="sb"> </param>
        /// <param name="appendMe"> </param>
        /// <param name="seperator"> </param>
        public static void AppendWithSeperator(this StringBuilder sb, string appendMe, string seperator = " ")
        {
            if (String.IsNullOrEmpty(appendMe)) return;
            if (sb == null) throw new ArgumentNullException("sb");
            sb.AppendIfNotNullOrEmpty(appendMe, seperator);
        }

        /// <summary>
        ///   Append if <paramref name="appendMe" /> is not <c>null</c> or empty.
        /// </summary>
        /// <param name="sb"> </param>
        /// <param name="appendMe"> </param>
        /// <param name="seperator"> </param>
        /// <returns> </returns>
        public static bool AppendIfNotNullOrEmpty(this StringBuilder sb, object appendMe, string seperator = null)
        {
            if (sb == null) throw new ArgumentNullException("sb");
            Contract.EndContractBlock();

            if (appendMe == null) return false;
            String am = appendMe.ToString();
            if (String.IsNullOrEmpty(am)) return false;

            if (seperator != null) sb.AppendIfBuilderIsNotEmpty(seperator);
            sb.Append(am);
            return true;
        }

        /// <summary>
        ///   Append <paramref name="appendMe" /> if the <c>StringBuilder</c> already
        ///   contains characters.
        /// </summary>
        /// <param name="sb"> </param>
        /// <param name="appendMe"> </param>
        /// <returns> </returns>
        public static bool AppendIfBuilderIsNotEmpty(this StringBuilder sb, string appendMe)
        {
            if (String.IsNullOrEmpty(appendMe)) return false;
            if (sb == null) throw new ArgumentNullException("sb");
            if (sb.Length > 0) {
                sb.Append(appendMe);
                return true;
            }
            return false;
        }

        /// <summary>
        /// Non ASCII characters will be stripped / removed.
        /// </summary>
        /// <param name="candidate"></param>
        /// <returns></returns>
        public static string ReplaceNonAsciiCharacters(this string candidate)
        {
            if (String.IsNullOrEmpty(candidate)) return candidate;
            // http://stackoverflow.com/questions/123336/how-can-you-strip-non-ascii-characters-from-a-string-in-c
            string ascii = Regex.Replace(candidate, @"[^\u0000-\u007F]", string.Empty);
            return ascii;
        }

        /// <summary>
        /// Remove non numeric chars, allow only 0-9
        /// </summary>
        /// <param name="candidate"></param>
        /// <returns></returns>
        public static string ReplaceNonNumericCharacters(this string candidate)
        {
            if (String.IsNullOrEmpty(candidate)) return candidate;
            return Regex.Replace(candidate, "[^.0-9]", string.Empty);
        }

        /// <summary>
        /// Remove any whitespaces
        /// </summary>
        /// <param name="candidate"></param>
        /// <returns></returns>
        public static string RemoveWhitespaces(this string candidate)
        {
            return String.IsNullOrEmpty(candidate) ? candidate : Regex.Replace(candidate, @"s", "");
        }

        /// <summary>
        ///   String to stream.
        /// </summary>
        /// <param name="input"> </param>
        /// <returns> </returns>
        //  EXTERNAL: http://www.csharp411.com/c-convert-string-to-stream-and-stream-to-string/
        public static MemoryStream StringToStream(this string input)
        {
            if (input == null) return new MemoryStream();

            // convert string to stream
            byte[] byteArray = Encoding.ASCII.GetBytes(input);
            MemoryStream stream = new MemoryStream(byteArray);
            return stream;
        }
        #endregion Methods
    }
}